#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include "main.h"
#include "keywordMatch.h"
#include "labelHandlers.h"

int addHaltCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lmanager, struct program * prog) {
    addProgramByte(prog, 0);
    return 0;
}

int addAddCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //add Rx
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_REGISTER;
    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if(value == RG_R0) {
        //error
        return -1;
    } else if (value == RG_R1) {
        machineByte = 1;
    } else if (value == RG_R2) {
        machineByte = 20;
    } else if (value == RG_R3) {
        machineByte = 21;
    }
    addProgramByte(prog, machineByte);
    return 0;
}

int addSubCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //sub Rx
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_REGISTER;
    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if(value == RG_R0) {
        //error
        return -1;
    } else if (value == RG_R1) {
        machineByte = 2;
    } else if (value == RG_R2) {
        machineByte = 22;
    } else if (value == RG_R3) {
        machineByte = 23;
    }
    addProgramByte(prog, machineByte);
    return 0;
}

int addIncCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //inc Rx
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_REGISTER;
    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if(value == RG_R0) {
        machineByte = 3;
    } else if (value == RG_R1) {
        machineByte = 4;
    } else if (value == RG_R2) {
        machineByte = 16;
    } else if (value == RG_R3) {
        machineByte = 18;
    }
    addProgramByte(prog, machineByte);
    return 0;
}

int addDecCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //dec Rx
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_REGISTER;
    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if(value == RG_R0) {
        machineByte = 5;
    } else if (value == RG_R1) {
        machineByte = 6;
    } else if (value == RG_R2) {
        machineByte = 17;
    } else if (value == RG_R3) {
        machineByte = 19;
    }
    addProgramByte(prog, machineByte);
    return 0;
}

int addClearCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //clear Rx
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_REGISTER;
    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if(value == RG_R0) {
        machineByte = 35;
    } else if (value == RG_R1) {
        machineByte = 36;
    } else if (value == RG_R2) {
        machineByte = 37;
    } else if (value == RG_R3) {
        machineByte = 38;
    } else if (value == RG_IP) {
        machineByte = 39;
    }
    addProgramByte(prog, machineByte);
    return 0;
}

int addMulCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //mul Rx
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_REGISTER;
    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if(value == RG_R0) {
        //error
        return - 1;
    } else if (value == RG_R1) {
        machineByte = 32;
    } else if (value == RG_R2) {
        machineByte = 33;
    } else if (value == RG_R3) {
        machineByte = 34;
    }
    addProgramByte(prog, machineByte);
    return 0;
}

int addBeepCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
            addProgramByte(prog, 7);
            return 0;
}

int addPrintCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //print something
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 8;
    int opByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_ASCII | INTYPE_DATA_ADDRESS | INTYPE_IMMEDIATE;
    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if (valueType & (INTYPE_DATA_ADDRESS | INTYPE_IMMEDIATE)) {
        opByte = value;
    } else if (valueType & (INTYPE_ASCII)) {
        opByte = ((char*)value)[0];
        free ((char*)value);
    }
    addProgramByte(prog, machineByte);
    addProgramByte(prog, opByte);
    return 0;
}

int addPrintaCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //print something
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 24;
    int opByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_ASCII | INTYPE_DATA_ADDRESS | INTYPE_IMMEDIATE;
    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if (valueType & (INTYPE_DATA_ADDRESS | INTYPE_IMMEDIATE)) {
        opByte = value;
    } else if (valueType & (INTYPE_ASCII)) {
        opByte = ((char*)value)[0];
        free ((char*)value);
    }
    addProgramByte(prog, machineByte);
    addProgramByte(prog, opByte);
    return 0;
}

int addPrintiCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //print something
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 25;
    int opByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_DATA_ADDRESS | INTYPE_LABEL_BYTE | INTYPE_LABEL_ARRAY | INTYPE_LABEL_LABEL | INTYPE_LABEL_IMMEDIATE;
    if(getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if (valueType & (INTYPE_DATA_ADDRESS | INTYPE_LABEL_BYTE | INTYPE_LABEL_ARRAY | INTYPE_LABEL_LABEL | INTYPE_LABEL_IMMEDIATE)) {
        opByte = value;
    }
    addProgramByte(prog, machineByte);
    addProgramByte(prog, opByte);
    return 0;
}

int addMovCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    int movType = 0;
    int machineByte = 0;
    int opByte = 0;
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int firstOpRegister = RG_INVALID;
    int secondOpRegister = RG_INVALID;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_REGISTER | INTYPE_DATA_ADDRESS | INTYPE_LABEL_BYTE | INTYPE_LABEL_ARRAY | INTYPE_BRACKET | INTYPE_LABEL_LABEL;
    getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false);

    if (valueType & (INTYPE_REGISTER)) {
        firstOpRegister = value;
    } else if (valueType & (INTYPE_ASCII)) {
        movType = 1;
        opByte = ((char*)value)[0];
        free ((char*)value);
    } else if (valueType & (INTYPE_LABEL_BYTE | INTYPE_LABEL_LABEL | INTYPE_DATA_ADDRESS | INTYPE_LABEL_ARRAY)) {
        movType = 1;
        opByte = value;
    } else if (valueType & (INTYPE_BRACKET)) {
        movType = 2;
        int mRegister = ((int*)value)[0];
        int mSign = ((int*)value)[1];
        char * temp = (char*)((int*)value)[2];
        free ((int*)value);
        allowTypes = INTYPE_DATA_ADDRESS | INTYPE_LABEL_BYTE | INTYPE_LABEL_ARRAY | INTYPE_IMMEDIATE;
        getValue (errorMsg, temp, lManager, allowTypes, &valueType, &value, &valueLength, false);
        if (mSign == 1) {
            opByte = value;
        } else if (mSign == 2) {
            opByte = (BYTE_SIZE - value) % BYTE_SIZE;
        }
        free(temp);
    }
    //BEGIN SECOND OPERAND
    getNextString (writeBuffer, pFile);
    if (movType == 0) { //mov register blah
        allowTypes = INTYPE_LABEL_BYTE | INTYPE_LABEL_ARRAY | INTYPE_LABEL_IMMEDIATE | INTYPE_BRACKET | INTYPE_DATA_ADDRESS| INTYPE_LABEL_LABEL;
    } else if (movType == 1 || movType == 2) { //mov blah register
        allowTypes = INTYPE_REGISTER;
    }
    getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false);

    if(valueType & (INTYPE_REGISTER)) {
        secondOpRegister = value;
    } else if (valueType & (INTYPE_LABEL_BYTE | INTYPE_LABEL_IMMEDIATE | INTYPE_LABEL_LABEL | INTYPE_DATA_ADDRESS | INTYPE_LABEL_ARRAY)) {
        opByte = value;
//    } else if (valueType & (INTYPE_ASCII)) {
//        opByte = ((char*)value)[0];
//        free ((char*)value);
    } else if (valueType & (INTYPE_BRACKET)) {
        movType = 3;
        int mRegister = ((int*)value)[0];
        int mSign = ((int*)value)[1];
        char * temp = (char*)(((int*)value)[2]);
        free ((int*)value);
        allowTypes = INTYPE_DATA_ADDRESS | INTYPE_LABEL_BYTE | INTYPE_LABEL_ARRAY | INTYPE_IMMEDIATE;
        getValue (errorMsg, temp, lManager, allowTypes, &valueType, &value, &valueLength, false);
        if (mSign == 1) {
            opByte = value;
        } else if (mSign == 2) {
            opByte = (BYTE_SIZE - value) % BYTE_SIZE;
        }
        free(temp);
    }
    if (movType == 0) {
        if (firstOpRegister == RG_R0) {
            machineByte = 9;
        } else if (firstOpRegister == RG_R1) {
            machineByte = 10;
        }
    } else if (movType == 1) {
        if (secondOpRegister == RG_R0) {
            machineByte = 11;
        } else if (secondOpRegister == RG_R1) {
            machineByte = 12;
        }
    } else if (movType == 2) {
        if (secondOpRegister == RG_R0) {
            machineByte = 41;
        } else if (secondOpRegister == RG_R1) {
            machineByte = 30;
        } else if (secondOpRegister == RG_R2) {
            machineByte = 31;
        } else if (secondOpRegister == RG_IP) {
            machineByte = 45;
        } else if (secondOpRegister == RG_FP) {
            machineByte = 43;
        }
    } else if (movType == 3) {
        if (firstOpRegister == RG_R0) {
            machineByte = 40;
        } else if (firstOpRegister == RG_R1) {
            machineByte = 28;
        } else if (firstOpRegister == RG_R2) {
            machineByte = 29;
        } else if (firstOpRegister == RG_IP) {
            machineByte = 44;
        } else if (firstOpRegister == RG_FP) {
            machineByte = 42;
        }
    }
    addProgramByte (prog, machineByte);
    addProgramByte (prog, opByte);
    return 0;
}

int addJmpCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //jmp Rx addr
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 13;
    int opByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_LABEL_LABEL | INTYPE_DATA_ADDRESS;
    if (getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if (valueType & (INTYPE_LABEL_LABEL | INTYPE_DATA_ADDRESS)) {
        opByte = value;
    }
    addProgramByte(prog, machineByte);
    addProgramByte(prog, opByte);
    return 0;
}

int addJzCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //jz Rx addr
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 0;
    int opByte = 0;
    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_REGISTER;
    if (getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if (value == RG_R0) {
        machineByte = 14;
    } else if (value == RG_R1) {
        machineByte = 26;
    } else if (value == RG_R2) {
        machineByte = 46;
    }
    getNextString (writeBuffer, pFile);


    allowTypes = INTYPE_LABEL_LABEL | INTYPE_DATA_ADDRESS;
    if (getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if (valueType & (INTYPE_LABEL_LABEL | INTYPE_DATA_ADDRESS)) {
        opByte = value;
    }
    addProgramByte(prog, machineByte);
    addProgramByte(prog, opByte);
    return 0;
}

int addJnzCode (char * errorMsg, FILE * pFile, char * writeBuffer, struct labelManager * lManager, struct program * prog) {
    //jnz Rx addr
    int value;
    int valueLength;
    int valueType;
    int allowTypes;

    int machineByte = 0;
    int opByte = 0;

    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_REGISTER;
    if (getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if (value == RG_R0) {
        machineByte = 15;
    } else if (value == RG_R1) {
        machineByte = 27;
    } else if (value == RG_R2) {
        machineByte = 47;
    }
    getNextString (writeBuffer, pFile);
    allowTypes = INTYPE_LABEL_LABEL | INTYPE_DATA_ADDRESS;
    if (getValue (errorMsg, writeBuffer, lManager, allowTypes, &valueType, &value, &valueLength, false) == -1){
        //error
        return -1;
    }
    if (valueType & (INTYPE_LABEL_LABEL | INTYPE_DATA_ADDRESS)) {
        opByte = value;
    }
    addProgramByte(prog, machineByte);
    addProgramByte(prog, opByte);
    return 0;
}
